#ifndef _OCCIACCESS_H_
#define _OCCIACCESS_H_

#include <buildspec.h>
#include <Client/ClientUtils/Network/AbstractRDBAccess.h>

namespace oracle
{
namespace occi
{
class Clob;
}
}

namespace GST {

	//prototypes:
	namespace ClientGUI	{
		class ProgressControl;
	}

namespace ClientUtils {

	//prototypes:
	class OCCIDriver;
	struct OCCIStatementHandle;
	typedef boost::shared_ptr<OCCIStatementHandle> OCCIStmtPtr;	

	struct GST_API_EXPORT OCCICopiedFeature
		: public CrudeFeature
	{
		std::stringstream geomValues;
		std::stringstream propValues;
		std::string lockId;
		TableAttributeValListPtr objProperties;

		virtual std::string GetGeometry() {  return geomValues.str(); }
		virtual std::string GetPropertyVals() {  return propValues.str(); }
		virtual std::string GetLockId() { return lockId; }
		virtual TableAttributeValListPtr GetObjProperties() { return objProperties; }
	};

	struct GST_API_EXPORT OraConfig
		: public RDBConfig
	{
		std::string serviceName;
		virtual bool doCompare(const DBConfig& other) const;

	};
	typedef boost::shared_ptr<OraConfig> OraConfigPtr;

	struct GST_API_EXPORT OraSPDesc
		: public SPDesc
	{
		std::string spRunByUser;

		OraSPDesc(const SPDesc & base)
			: SPDesc(base)
			, spRunByUser("")
		{}
		OraSPDesc()
			: SPDesc()
		{}
	};
	typedef boost::shared_ptr<OraSPDesc> OraSPDescPtr;

	/**
	 * Base class handle NetworkInterface functions and implement those to direct connected
	 * Relational Database backend. Its derived classes need overload functions which
	 * using SQL slang or DB specific behavior like getCurrentUser().
	 */
	class GST_API_EXPORT OCCIAccess
		: public AbstractRDBAccess
	{
	public:
		OCCIAccess( HostPtr host, const std::string& service, OraConfigPtr dbconfig );
		OCCIAccess( const OCCIDriver& driver, OraConfigPtr dbconfig );
		
		///@name DB specific NetworkInterface implementations
		//@{
		virtual NetworkPtr fork() const;
		virtual NetworkPtr reconnect(const std::string & user, const std::string & pwd, const std::string & db="") const;
		virtual bool isSuperUserConnection() const;
		virtual UserPtr getCurrentGSTUser() const;
		virtual BackendType GetBackendType() const;
		
		///@name DB specific NetworkInterface implementations
		//@{
		virtual std::string MakeValidTableName( const std::string tableNameStump, 
			const std::string & suffix = std::string(""), bool quote_tableName = true, 
			int randomChars = 3 ) const;
		virtual void CreateFeatureClass_Impl( const FeatureClassDetailedDesc& featureClassDesc );
		virtual void CreatePropertyTable( const FeatureClassDesc& featureClass, const GeoColumnDesc & geocolumn );
		virtual CrudeFeaturePtr GetFeature( const FeatureDesc & feature , const IGeometry::RequestFormat & geometryformat, Geometry::SRSPtr srs, bool lock = false, QueryBoxPtr queryBox = QueryBoxPtr(), long commitKey = -1 );
		virtual void SimplifyFeature( const FeatureDesc & feature );
		virtual FeatureDescPtr UploadNewFeature( const Geometry::StreamableGeometry & feature, const FeatureClassDesc & target, long commitKey = -1, bool updateStats = true );
		virtual void UpdateFeature( const Geometry::StreamableGeometry & feature, const std::string & lockid, const FeatureClassDesc & target, long commitKey = -1, bool updateStats = true );
		virtual void UpdateFeatureKeepLock( const Geometry::StreamableGeometry & feature, const std::string & lockid, const FeatureClassDesc & target, long commitKey = -1, bool updateStats = true );
		//virtual void DeleteFeatureClass( const std::string & featureClassName );
		virtual void UnlockByKey( const std::string & key );
		virtual void ExecuteScript( const std::string & script );
		virtual CrudeFeaturePtr GetFeatureBinary( const FeatureDesc & feature, Geometry::SRSPtr srs, QueryBoxPtr queryBox = QueryBoxPtr(), long commitKey = -1);
		virtual	void CreateTable( const GST::ClientUtils::TableAttributeDescList & attributes
			, bool quoteTablename = true );
		virtual void InsertRecord( const GST::ClientUtils::TableAttributeValList & attributes
			, bool quoteTablename = true );

		virtual void DropTable( const std::string & tableName
			, bool quoteTablename = true );

		virtual NamesListPtr ListAllTables() const ;
		virtual bool ViewRefersTable(const std::string& tablename, const std::string& viewname) const;
		virtual NamesListPtr ListAllViewsRefersTable(const std::string& tablename) const;

		virtual NamesListPtr ListAllColumns(const std::string& tablename) const;
		virtual NamesListPtr ListPrimaryKeyColumns(const std::string& tablename) const;
		virtual std::string GetExactColumnDataType(const std::string& columnname) const;
		virtual ClientUtils::TableAttributeDesc::DType GetColumnDType(const std::string& columnname) const;
		virtual void RenameProperty_Impl( const FeatureClassDesc& featureClass, TableAttributeDescPtr attribute, const std::string& newName ) const;

		virtual ServiceDescriptionPtr CreateService(const std::string &name, const std::string &url, long accessLevelId, const std::string &user, const std::string &secret) const;
		virtual ServiceDescriptionPtr UpdateService(long serviceId, const std::string &name, const std::string &url, long accessLevelId, const std::string &user, const std::string &secret) const;

			///@name user handling
			//@{
				virtual void CreateUser(const User& user, const std::string & initialgroupName);
				virtual void DropUser(const std::string & name);
				virtual void CreateGroup(const Group& group);
				virtual void DropGroup(const std::string & name);
				virtual void AddUserToGroup(const std::string& userName, const std::string& groupName);
				virtual void DropUserFromGroup(const std::string& userName, const std::string& groupName);

				virtual UserListPtr GetUsersByGroup( const std::string& groupName ) const;
				virtual GroupListPtr GetGroupsByUser( const std::string& userName) const;
				virtual GroupListPtr GetGroupsByCurrentUser() const;
				virtual void ChangeUserPassword(const std::string& userName, const std::string &newPassword, const std::string &oldPassword) const;
			//@}
		//@}

		///@name Version handling
		//@{
		virtual Utils::GSTVersionPtr GetStorageVersion() const;
		virtual int CheckCompatibility(const std::string &client_version) const;
		//@}

		///@name MoMa
		//@{
		virtual LinkAdjacencyPtr CreateLinkAdjacency(LinkAdjacency::LinkId parent, LinkAdjacency::ReferId refId, LinkAdjacency::TargetType refTarget, Owner::OwnerId owner) const;
		virtual ColorScale::ColorScaleId AddColorScale(ColorScalePtr cs) const;
		virtual ColorValue::ColorValueId AddColorValue(ColorValuePtr cv) const;
		virtual void CreateElement(ElementPtr Elment, bool uniqueName = false) const;
		virtual FeatureLinkIdGeometryIdPairListPtr ResolveGeometries( LinkAdjacency::LinkId rootElement, LinkAdjacency::LinkId csRootElement = ROOT_NODE_LINKID, ColorScale::ColorScaleId csRootScale = -1 ) const;
		virtual CrudeFeaturePtr GetFeatureFromMoMa(	LinkAdjacency::LinkId flid
			, const Geometry::IGeometry::RequestFormat & geometryformat
			, Geometry::SRSPtr srs
			, LinkAdjacency::LinkId csRootElement = ROOT_NODE_LINKID
			, ColorScale::ColorScaleId csRootScale = -1
			, bool lock = false
			, QueryBoxPtr queryBox = QueryBoxPtr() 
			, long commitKey = -1) const;
		virtual QueryBoxPtr GetBboxFromMoma(const MoMaKey &momakey,
											Geometry::SRSPtr targetSrs) const;
		virtual MoMaPropertyDescPtr CreateMoMaPropertyDesc(const std::string& name, TableAttributeDesc::DType dtype, Owner::OwnerId owner) const;
		virtual MoMaPropertyValueListPtr GetMoMaPropertyValuesForNode(const LinkedElement& element) const;

		//@}

		///@name Versioning
		//@{
		virtual long BeginCommit(const std::string &message) const;
		virtual int EndCommit(long commitKey) const;
		//@}

		///@name AbstractRDBAccess interface
		//@{
		virtual RDBDriverInterface * getRDBDriver() const;
		virtual void RunPreparedInsertStatement(const InsertStatementDesc & preparedStatement);
		virtual void RunPreparedUpdateStatement(const UpdateStatementDesc & preparedStatement);
		//@}

		///@name Oracle specific stuff
		//@{
		void switchDb( const std::string & schemaName );
		//@}

		virtual void updateSchemaStats() const;

	protected:

		///@name virtual helpers
		//@{
		virtual AbstractRDBAttributeVal * newAttributeVal(const TableAttributeVal & val) const;
		virtual AbstractRDBAttributeDesc * newAttributeDesc(const TableAttributeDesc & desc, bool is_primaryKey=false) const;
		virtual void RunSP(SPDescPtr storedProcedure) const;
		virtual void RenameFeatureClass_Impl( const FeatureClassDesc& featureClassDesc, const std::string& newName );
		//@}

		///@name local helpers
		//@{

		inline std::string quoteTablename(const std::string& tableName, const bool quoteTablename);
		std::string getRoleName(const std::string& groupName);

		void cleanClobTemplateTable(int id) const;
		void cleanBlobTemplateTable(int id) const;
		void TryToRollback(OCCIStmtPtr stmt, const std::string & sql = std::string("")) const;
		static std::string PrepareScript(const std::string & script);
		static std::string & SQLFormat_PassAsParameter(std::string & sql, const std::string & functionName, const std::string & quoter = std::string("''"));
		static std::string GetUserNameFromOraIdentifer(const std::string & identifer);
		bool OraTableExists(const std::string& tablename) const;
		void RunSP(OraSPDescPtr oraStoredProcedure) const;

		virtual TableAttributeDescListPtr GetPrimaryKey(const std::string & tablename) const;
		void UpdateFeature_impl( const Geometry::StreamableGeometry & feature, const std::string & lockid, bool keepLock, const FeatureClassDesc & target, long commitKey = -1, bool updateStats = true );

		virtual std::string getEndpointTargetIdentifer() const;

	private:
		/**
		  * Constructs SQL from attributes and variousProperties
		  *
		  * @param updateSyntax			controls the output behavior 
		  *								if true the ouput will be generated like this
		  *								\code
										UPDATE TABLE blub SET (<ss_variousPropertiesNames>)
										\endcode

										if updateSyntax == false the output will 
										be like this
										\code
										INSERT INTO blub (<ss_variousPropertiesNames>)
										VALUES (<variousPropertiesValues>)
										\endcode
		  * @param variousProperties	the property values to be written to SQL
		  * @param attributes			the attributes used to determine the data 
		  *								type (match by name of variousProperties)
		  * @param variousPropertiesNames output parameter (see param updateSyntax)
		  * @param variousPropertiesValues output parameter 2 (see param updateSyntax)
		  * @param ignore				a property name that will be ignored
		  * @param warn_methodname				some identifer to be written into warning
		  *								(if they occur)
		  * @param executeimmediatesyntax	true if the statement is executed inside
		  *									a execute immidiate 'SQL'; (then all
		  *									quotes become escaped) 
		  */
		virtual void constructVariousPropertyNameAndValueList( bool updateSyntax
			, Geometry::StreamableGeometry::KeyValueListPtr variousProperties
			, TableAttributeDescListPtr attributes
			, std::string &variousPropertiesNames
			, std::string &variousPropertiesValues
			, const std::string & ignore
			, const std::string & warn_methodname
			, const bool executeimmediatesyntax = false ) const;
		virtual GST::Protocol GetProtocol() const;
		boost::function<std::string&(std::string&)> CreateRunDDL(bool isPublic, const std::string& user);
		virtual void AppendConstrainedProperty_impl(const FeatureClassDesc& featureClass, const std::string& propertyName, const TableConstrainedAttributeDesc& attribute, const bool isObjectProperty);

		///@name SQL generation helper
		//@{
		virtual void GenSQLForeignKeyConstraint(ForeignKeyPtr kc, const std::string& cpname, std::string& sql_OUT);
		virtual std::string GenSQLAddColumn( const std::string & table, const TableAttributeDesc& attribute );
		virtual std::string GenSQLDeleteColumn(ForeignKeyPtr fk);
		virtual std::string GenSQLBeginTransaction();
		virtual std::string GenSQLEndTransaction(const std::string& errormessage="");
		std::string GenSQLAddTriggerAndSequenceForTypeSerial(const TableAttributeDescListPtr list
			, boost::function<std::string&(std::string&)> & RunDDL
			, const std::string & groupAccountName
			, bool quoteTablename = true );

		std::string GenSQLAddTriggerAndSequence(const std::string & tableName
			, const std::string & attributeName
			, boost::function<std::string&(std::string&)> & RunDDL
			, const std::string & groupAccountName
			, bool quoteTablename = true );
		//@}

	protected:
		void handleExceptions(OCCIStmtPtr stmt, OCCIDriver *driver, const std::string &functionName, const std::string &sprocName = std::string()) const;

		///@name Slices/Sections helper
		//@{
		virtual ColorMap::Id InsertColorMap(  const std::string & name, const Geometry::IGeometry::Color & nodataColor, const std::string &regex, AccessLevel accessLevel );
		int writeStringToClob(unsigned int row, const std::string &value, OCCIStmtPtr stmt) const;
		int writeVecToBlob(unsigned int row, const std::vector<unsigned char> &value, OCCIStmtPtr stmt) const;
		//@}

		///@name easy cast base members
		//@{
		OCCIDriver * getDriver() const;
		OraConfigPtr getConfig() const;
		//@}
	};

	void getStringFromClob(unsigned int row, std::stringstream &result, OCCIStmtPtr stmt);
	void getStringFromClob(unsigned int row, std::string &result, OCCIStmtPtr stmt);
	void getVecFromBlob(unsigned int row, std::vector<unsigned char> &result, OCCIStmtPtr stmt);

} //of ns ClientUtils
} //of ns GST

#endif //_OCCIACCESS_H_
